import logging

from os import path
from pprint import pformat

import markdown2
import requests

import lakesuperior

from flask import (
        Blueprint, make_response, redirect,
        render_template, send_file, url_for)
from lakesuperior.api import resource as rsrc_api
from lakesuperior.dictionaries.namespaces import ns_collection as nsc
from memoization import cached

from grayspread import basedir, helpers, store
from grayspread.draw import draw_dimensions, draw_neighbor_graph
from grayspread.store import txn_ctx


logger = logging.getLogger(__name__)

main = Blueprint('main', __name__, template_folder='templates')


@main.route('/', methods=['GET'])
def index():
    """Homepage."""
    hl_uids = store.random_uids(3)
    logger.info(f'Highlight UIDs: {hl_uids}')
    with txn_ctx():
        highlights = {
                type_: helpers.link_meta(uid)
                for type_, uid in hl_uids.items()}
    with txn_ctx():
        return render_template('index.html', highlights=highlights, nsc=nsc)


@main.route('/browse/type/<type_>')
def browse_type(type_):
    """ Browse all resources of a category. """
    if type_ not in store.highlight_types:
        return f'Category not found: {type_}', 404
    with txn_ctx():
        rsrcs = {
                helpers.link_meta(uri)
                for uri in store.get_all_uids(nsc['lii'][type_])}
        type_meta = helpers.type_meta(type_)

        return render_template(
                'model/type_index.html', type_label=type_,
                type_meta=type_meta, rsrcs=rsrcs)


@main.route('/view/random')
def random_pick():
    """ View a random resource. """
    uid = {*store.random_uids(1).values()}.pop()

    return view(uid)


@main.route(
        '/page/local:<local_uid>', methods=['GET'],
        defaults={'action': 'page'})
@main.route(
        '/view/local:<local_uid>', methods=['GET'],
        defaults={'action': 'view'})
def view_local(action, local_uid):
    """ View a resource or page from a given local UID. """
    try:
        uid = helpers.uid_from_local_uid(local_uid)
    except KeyError:
        return (f'Document id not found: {local_uid}', 404)

    if action == 'view':
        return view(uid)
    elif action == 'page':
        return view_page(uid)
    else:
        return (f'Not a valid action: {action}', 404)


@main.route('/articles/<uid>')
@main.route('/cameras/<uid>')
@main.route('/projects/<uid>')
@main.route('/view/legacy/<uid>')
def view_legacy(uid):
    """ View a resource by its legacy UID. """
    try:
        uid = store.get_legacy_uid(uid)
    except KeyError:
        return f'Legacy UID not found: {uid}', 404

    # Permanent redirect.
    return redirect(url_for('main.view', uid=uid.lstrip('/')), 301)


@main.route('/view/<path:uid>', methods=['GET'])
def view(uid):
    """ View an individual resource. """
    # Normalize UID.
    uid = '/' + uid.lstrip('/')
    rsrc = store.get_resource(uid)
    with txn_ctx():
        trp = {*rsrc.imr}
        types = rsrc.types

    logger.info(f'Triples: {pformat(trp)}')

    if nsc['ore']['Aggregation'] in types:
        return view_aggregation(rsrc)

    lii_type = helpers.get_lii_type(rsrc)
    lii_type_str = lii_type.split('#')[-1] if lii_type else 'generic'
    with txn_ctx():
        return render_template(
                f'model/{lii_type_str.lower()}.html',
                rsrc=rsrc, lii_type=lii_type_str,
                title=lii_type + ': ' + helpers.get_value(
                        rsrc, nsc['skos'].prefLabel),
                rel_media=helpers.get_related_media(rsrc))


def view_aggregation(rsrc):
    """
    View a media aggregation.

    Not directly reachable by route.
    """
    with txn_ctx():
        return render_template(
                'model/aggregation.html', rsrc=rsrc,
                ars=helpers.aggregation_list(rsrc.uid))


@main.route('/page/<path:uid>', methods=['GET'])
def view_page(uid):
    """ Render a Markdown-formatted text document in store as a HTML page. """
    content = md_doc_to_html(uid)
    return render_template(
            'page.html', title=content.metadata['title'], content=content)


@main.route('/image/<path:path>')
def stream_iiif_image(path):
    """ Forward a IIIF image sending data in chunks. """
    parts = path.lstrip('/').split('/', 1)
    uuid = '/' + parts[0]
    params = parts[1]
    logger.debug("Image forwarding with params: {params}.")
    with txn_ctx():
        iiif_rsp = requests.get(
                helpers.image_iiif_url(uuid, params), stream=True)

    fwd_header_names = (
        "Cache-Control", "Last-Modified", "Content-Disposition",
    )
    fwd_headers = {}
    for hname in fwd_header_names:
        if hname in iiif_rsp.headers:
            fwd_headers[hname] = iiif_rsp.headers[hname]
    mimetype = iiif_rsp.headers.get('Content-Type')

    return helpers.stream(iiif_rsp, mimetype, fwd_headers)


@main.route('/content/<path:uid>')
def stream_content(uid):
    """ Stream out a LDP-NR from LSup. """
    uid = '/' + uid
    try:
        rsrc = rsrc_api.get(uid)
    except lakesuperior.exceptions.ResourceNotExistsError:
        return f'Resource {uid} not found.', 404

    with txn_ctx():
        rsp = make_response(send_file(
                rsrc.local_path, as_attachment=True,
                attachment_filename=rsrc.filename,
                mimetype=rsrc.mimetype), 200)

    return rsp


@main.context_processor
def base_tpl_vars():
    """ Variables available in all templates. """
    return {
        'basename': path.basename,
        'fmt_dim': helpers.fmt_dimensions,
        'draw_dimensions': draw_dimensions,
        'draw_neighbor_graph': draw_neighbor_graph,
        'inv_rel': helpers.inv_rel,
        'link_meta': helpers.link_meta,
        'nsc': nsc,
        'types': store.highlight_types,
        'value': helpers.get_value,
        'values': helpers.get_values,
        'aggregation_list': helpers.aggregation_list
    }


@cached(ttl=60)
def md_doc_to_html(uid):
    """ Retrieve Markdown content from a LS resource and return HTML. """
    rsrc = store.get_resource(uid)

    with txn_ctx():
        content = rsrc.content.read().decode()

    return markdown2.markdown(content, extras=['metadata'])


@cached(ttl=60)
def md_page_to_html(page_id):
    """ Retrieve Markdown content from static page file and return HTML. """
    with open(f'{basedir}/{main.template_folder}/pages/{page_id}.md') as fh:
        content_md = fh.read()

    return markdown2.markdown(content_md, extras=['metadata'])
